/*
    Speedbump aka Project Kimbula
    Copyright (C) 2010  Ping Shin Ching

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses.

	Ping Shin Ching
	righteous.ninja@gmail.com OR ping_sc@hotmail.com
	More information at http://www.tidytutorials.com/p/speedbump.html
 */

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "connlimsim.h"


ConnLimitFilter::ConnLimitFilter(){
		
}

/*************************************************************************
 * Initialize the Connection Limit filter with maximum simultaeneous
 * connections
 ************************************************************************/
int ConnLimitFilter::InitFilter(InitInterface* fip){
	const char* fir	= fip->GetInitParameter("connlimit-msg");
	noofips =0;
	hitcounts = 0;
	maxsimultaeneous= atoi((const char*)fip->GetInitParameter("connlimit"));
	
	if((fir)){
		filterRedirect	= (char*)malloc(strlen(fir)+1);
		memcpy(filterRedirect, fir, strlen(fir)+1);
	}
	else
		filterRedirect=0;

	return 0;
}

/*************************************************************************
 * Get the filter name
 *************************************************************************/
const char* ConnLimitFilter::GetFilterName(){
	return "Connection-Limiter";
}

/*************************************************************************
 * Get the redirect url
 *************************************************************************/
const char*	ConnLimitFilter::GetFilterRedirect(){
	return filterRedirect;
}

/*************************************************************************
 * Get the filter type - When it is to be called
 *************************************************************************/
int ConnLimitFilter::GetFilterType(){
	return FILTER_CHECK|FILTER_START|FILTER_END|FILTER_FREE|FILTER_DUMP;
}

/*************************************************************************
 * Destructor
 ************************************************************************/
ConnLimitFilter::~ConnLimitFilter(){
	flogit()<<"Connlimit Freeing"<<flend;
	if(hitcounts!=0){
		free(hitcounts);
		hitcounts = 0;
	}
	if(filterRedirect)
		free(filterRedirect);
}

/*************************************************************************
 * The actual filter function
 *************************************************************************/
int ConnLimitFilter::DoFilter(const char * ip, FILTER_TYPE type, FilterData* param1){

	int i;
	int retvalue=0;
	
	time_t now;

	long oldesttime;
	long oldestindex;

	now	= time(0);
	oldesttime = now;
	oldestindex	= -1;
	

	switch(type){

		case FILTER_START:
			if(ip==0)
				return 0;
	
			for( i = 0 ; i < noofips ; i++){ 
				if(hitcounts[i].hits ==0 && hitcounts[i].lastupdatedtime < oldesttime){
					oldesttime = hitcounts[i].lastupdatedtime;
					oldestindex = i;
				}
				if(FilterChain::CompareTwoIPs(ip, (const char*)hitcounts[i].ipaddress)){

					hitcounts[i].lastupdatedtime = now;
			
					if(hitcounts[i].hits < maxsimultaeneous){
						flogit()<<"Connlimit under "<<ip<<" "<<hitcounts[i].hits<<flend;
						hitcounts[i].hits++;
						retvalue = 1;

						calculateConnRate(param1, hitcounts[i].hits);
						flogit()<<"Connlimit up:"<<((FilterData*)param1)->ratelimitup<<" down:"<<((FilterData*)param1)->ratelimitdown<<flend;
						
						goto THE_END;
					}
					else{
						flogit()<<"Connlimit over "<<ip<<" "<<hitcounts[i].hits<<flend;
						retvalue = 0;
						goto THE_END;
					}
				}
			}

			if(noofips+1 > MAX_CONNLIMIT_IPS ){
				flogit()<<"Connlimit arraysize reached"<<flend;

				if(oldestindex!=-1){
					flogit()<<"Connlimit "<< ip <<" replaced with "<< hitcounts[oldestindex].ipaddress <<flend;
					
					FilterChain::AssignIP((char*)hitcounts[oldestindex].ipaddress, FILTER_ADDRSTRLEN, ip);
					hitcounts[oldestindex].lastupdatedtime = now;
					hitcounts[oldestindex].hits=1;
				}
				else{
					flogerr()<<"Connlimit auto pass"<<flend;
				}
				retvalue=1;
			}
			else{
				flogit()<<"Connlimit new "<<noofips<<flend;
				if(connlimRealloc()!=0){
					FilterChain::AssignIP((char*)hitcounts[noofips].ipaddress, FILTER_ADDRSTRLEN, ip);
			
					hitcounts[noofips].lastupdatedtime = now;
					hitcounts[noofips].hits=1;
			
					noofips++;
				}
				retvalue = 1;

			}
			break;


		case FILTER_END:
			if(ip==0)
				return 0;

			for( i = 0 ; i < noofips ; i++){ 
				if(FilterChain::CompareTwoIPs(ip, (const char*)hitcounts[i].ipaddress)){
					flogit()<<"Connlimit finished "<<ip<<" "<< hitcounts[i].hits<<flend;
					if(hitcounts[i].hits>0){
						hitcounts[i].hits--;
					}
					retvalue=1;
					goto THE_END;
				}
			}
			break;


		case FILTER_FREE:
			//Not required
			retvalue = 1;
			break;


		case FILTER_DUMP:
			retvalue=1;
			break;


		default:
			flogit()<<"Connlimit unknown filter command"<<flend;
			retvalue=1;
			break;
	}

THE_END:
	return retvalue;
}


/*************************************************************************
 * Used to calculate the connection rate amongst the number of connections
 *************************************************************************/
void ConnLimitFilter::calculateConnRate(void* vp, int hits){
	FilterData * fd = (FilterData*)vp;
	if(hits!=0){
		fd->ratelimitdown = ((float)fd->ratelimitdown)/((float)hits);
		fd->ratelimitup = ((float)fd->ratelimitup)/((float)hits);
	}
}


/*************************************************************************
 * ConnLimit Realloc function for IPs
 *************************************************************************/
int ConnLimitFilter::connlimRealloc(){
	HitCount* tempp;
	if((tempp = (HitCount*)realloc(hitcounts, (noofips+1)*sizeof(HitCount)))==0){
		flogit()<<"Connlimit realloc failed"<<flend;
		return 0;
	}
	else{
		hitcounts = tempp;
		return 1;

	}
}
